package web

import (
	"boticz/app/global/consts"
	"boticz/app/global/variable"
	"boticz/app/http/entity/request"
	"boticz/app/model/users"
	"boticz/app/service/users/login_policy"
	"boticz/app/utils/alibaba"
	"boticz/app/utils/response"
	"encoding/json"
	"fmt"
	"time"

	_ "boticz/app/http/entity/response"
	userstoken "boticz/app/service/users/token"
	validator "boticz/app/utils/validator"

	"github.com/gin-gonic/gin"
)

type Users struct {
}

// @Summary 1. 登录
// @Description 支持不同方式登录方式 扫码登录、短信登录、一键登录等
// @Tags UserController
// @Accept  json
// @Produce  json
// @Param request body request.RqUserLogin userLogin "请求体"
// @Success 200 {object} response.RUsersModel "返回结果"
// @Failure 404 {string} string "User not found"
// @Router /user/login [post]
func (u *Users) UserLogin(context *gin.Context) {
	var body request.RqUserLogin
	if !body.Validate(context) {
		return
	}

	userName := body.UserName
	pass := body.Pass
	phone := body.Phone

	// 登录安全策略是否开启
	loginPolicyIsOpen := variable.ConfigYml.GetInt64("LoginPolicy.IsOpenPolicy")
	// 登录之前检查账号是否被登录策略禁用
	var lpf *login_policy.UserLoginPolicy
	if loginPolicyIsOpen == 1 {
		lpf = login_policy.CreateUsersLoginPolicyFactory()
		if lpf != nil {
			defer lpf.ReleaseRedisConn()
			if forbidden, err := lpf.CheckAccountIsForbidden(userName); err == nil && forbidden {
				response.Fail(context, consts.CurdLoginFailCode, "该账号已被登录策略自动禁止登录, 请至少等待2分钟后再试", "")
				return
			}
		}
	}
	fmt.Println("授权类型：", body.GrantType)

	var userModel *users.UsersModel
	userFactory := users.CreateUserFactory("")
	switch body.GrantType {
	case request.Password:
		fmt.Println("用户名和密码：", userName, pass)
		u := userFactory.LoginWithPassword(userName, pass)
		if u == nil {
			response.Fail(context, consts.CurdLoginFailCode, "账号或密码错误", "")
		}
		userModel = u
	case request.SmsCode:
		u, err := userFactory.LoginWithSmsCode(phone, body.Code)
		if err != nil {
			response.Fail(context, consts.CurdLoginFailCode, "验证码错误", err.Error())
			return
		}
		if u == nil {
			response.Fail(context, consts.CurdLoginFailCode, "没有查询到用户信息", "")
			return
		}
		userModel = u
	case request.ScanCode:
		u, err := userFactory.LoginWithScanCode(body.Code)
		if err != nil {
			response.Fail(context, consts.CurdLoginFailCode, "验证码错误", err.Error())
			return
		}
		if u == nil {
			response.Fail(context, consts.CurdLoginFailCode, "没有查询到用户信息", "")
			return
		}
		userModel = u
	default:
		fmt.Println("不支持的登录类型", body.GrantType)
		response.Fail(context, consts.CurdValidType, "不支持的登录类型", "")
		return
	}

	if userModel != nil {
		// 成功登录调用登录策略，自动会将之前的失败次数清0
		if loginPolicyIsOpen == 1 && lpf != nil {
			_, _ = lpf.SetAccountLoginCache(userName, false)
		}

		userTokenFactory := userstoken.CreateUserFactory()
		if userToken, err := userTokenFactory.GenerateToken(userModel.Id, userModel.UserName, userModel.Phone, variable.ConfigYml.GetInt64("Token.JwtTokenCreatedExpireAt")); err == nil {
			if userTokenFactory.RecordLoginToken(userToken, context.ClientIP()) {
				data := gin.H{
					"id":         userModel.Id,
					"user_name":  userModel.UserName,
					"real_name":  userModel.RealName,
					"phone":      userModel.Phone,
					"token":      userToken,
					"updated_at": time.Now().Format(variable.DateFormat),
				}
				response.Success(context, consts.CurdStatusOkMsg, data)
				return
			}
		} else {
			fmt.Println("生成token出错：", err.Error())
		}
	}
	if loginPolicyIsOpen == 1 && lpf != nil {
		// 登录失败 - 调用登录策略，自动会将之前的失败次数+1
		_, _ = lpf.SetAccountLoginCache(userName, true)
	}
	response.Fail(context, consts.CurdLoginFailCode, "登录失败", "")
}

// @Summary 2. 发送验证码
// @Description 调用阿里云短信服务发送短信
// @Tags UserController
// @Accept  json
// @Produce  json
// @Param request body request.RqSendCode userLogin "请求体"
// @Success 200 {string} string "返回结果"
// @Router /user/sendCode [post]
func (u *Users) SendCode(context *gin.Context) {
	var req request.RqSendCode
	if !req.Validate(context) {
		return
	}

	code := alibaba.GenerateSMSCodeWithoutLeadingZero(4)
	// 记录验证码到Redis中
	var lpf *login_policy.UserLoginPolicy = login_policy.CreateUsersLoginPolicyFactory()
	if lpf != nil {
		defer lpf.ReleaseRedisConn()
		if err := lpf.SetSmsCode(req.Phone, code); err != nil {
			response.Fail(context, consts.SmsCaptchaSendFailCode, "验证码验证错误", err.Error())
			return
		}
	}

	// 发送验证码
	templateCode := variable.ConfigYml.GetString("AliSMS.sendCode.templateCode")
	signName := variable.ConfigYml.GetString("AliSMS.sendCode.signName")
	params := map[string]string{
		"code": code,
	}

	templateParam, err := json.Marshal(params)
	if err != nil {
		response.Fail(context, consts.SmsCaptchaSendFailCode, "JSON转换失败", "")
		return
	}
	err = alibaba.SendSMS(req.Phone, signName, templateCode, string(templateParam))
	if err != nil {
		response.Fail(context, consts.SmsCaptchaSendFailCode, "发送验证码失败", err.Error())
		return
	}

	response.Success(context, "发送成功", "")
}

// 3. 获取用户信息
func (u *Users) GetUserInfo(context *gin.Context) {

}

// @Summary 4. 扫码登录 - 检查扫码结果
// @Description 检查扫码结果
// @Tags UserController
// @Accept  json
// @Produce  json
// @Param request body request.RqScanCode userLogin "请求体"
// @Success 200 {object} response.RScanCode "返回结果"
// @Router /user/checkScanQr [get]
func (u *Users) CheckScanQrStatus(context *gin.Context) {
	var req request.RqScanCode
	if !req.Validate(context) {
		return
	}
	// 查询扫码结果
	var lpf *login_policy.UserLoginPolicy = login_policy.CreateUsersLoginPolicyFactory()
	status := "0"
	if lpf != nil {
		defer lpf.ReleaseRedisConn()
		value, err := lpf.GetQrLoginCode(req.Code)
		if err != nil {
			response.Fail(context, consts.ScanCodeLoginGetFailCode, "查询检查扫码结果错误", err.Error())
			return
		}
		if validator.IsNotEmpty(value) {
			status = "1"
		}
	}
	data := gin.H{
		"code":   req.Code,
		"status": status,
	}
	response.Success(context, consts.CurdStatusOkMsg, data)
}

// @Summary 4. 扫码登录 - 获取扫码二维码
// @Description 获取扫码二维码
// @Tags UserController
// @Accept  json
// @Produce  json
// @Param request body request.RqScanCode userLogin "请求体"
// @Success 200 {object} response.RScanCode "返回结果"
// @Router /user/scanQr [get]
func (u *Users) GetScanQr(context *gin.Context) {
	code := alibaba.GenerateSMSCodeWithoutLeadingZero(10)
	// 记录验证码到Redis中
	var lpf *login_policy.UserLoginPolicy = login_policy.CreateUsersLoginPolicyFactory()
	if lpf != nil {
		defer lpf.ReleaseRedisConn()
		if err := lpf.SetQrLoginCode(code, ""); err != nil {
			response.Fail(context, consts.ScanCodeLoginGetFailCode, "获取扫码二维码错误", err.Error())
			return
		}
	}

	schema := variable.ConfigYml.GetString("QrLogin.schema")
	data := gin.H{
		"url": schema + code,
	}
	response.Success(context, consts.CurdStatusOkMsg, data)
}
